﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data;
using System.Data.Entity;
using Framework.Comum.DAL;
using System.Linq.Expressions;
using System.Data.Objects;
using System.Data.Objects.DataClasses;
using System.Data.Metadata.Edm;
using System.Reflection;
using Framework.Comum.Pesquisa;


namespace NaviCar.DB.Repositorio
{
    public class RepositorioBase<TEntity> : IRepositorio<TEntity> where TEntity : class, new()
    {
        private NaviCarDBContainer db;
        private DbSet<TEntity> dbSet;

        public RepositorioBase()
        {
            IGerenciadorContexto contexto =  ConfiguradorRepositorio.GerenciadorDB();

            this.db = (NaviCarDBContainer) contexto.RecuperarInstancia();

            //Verficar um Lock de Thread para impedir que mais de uma classe de dados seja criada por sessão
            //este erro pode acontecer em execução multipla por sessão não por aplicação, ou seja se dois usuáios
            //diferentes acessarem ao mesmo tempo o erro não ocorre, mas a chance de erro é muito remota.
            if (this.db == null)
            {
                this.db = new NaviCarDBContainer();
                contexto.DefinirInstancia(this.db);
            }

            this.dbSet = this.db.Set<TEntity>();
        }

        public void Fechar()
        {
            this.db.Database.Connection.Close();
        }

        public void Incluir(TEntity entidade)
        {
            if(entidade == null)
            {
                throw new NullReferenceException(string.Format("A Classe não foi criada."));
            }
				
			this.dbSet.Add(entidade);
        }

        public virtual void Detach(object entidade)
        {
            this.db.Entry(entidade).State = EntityState.Detached;

        }

        public virtual void Alterar(TEntity entidade)
        {
            
            this.dbSet.Attach(entidade);
            this.db.Entry(entidade).State = EntityState.Modified;
        }

        public virtual int Salvar()
        {
            return this.db.SaveChanges();
        }


        public void Excluir(params object[] ids)
        {
            TEntity entityToDelete = dbSet.Find(ids);
            Excluir(entityToDelete);
        }

        public void Excluir(TEntity entidade)
        {
            if (this.db.Entry(entidade).State == EntityState.Detached)
            {
                this.dbSet.Attach(entidade);
            }

            this.dbSet.Remove(entidade); 
        }

        public TEntity RecuperarPorID(params object[] ids)
        {
            TEntity retorno = this.dbSet.Find(ids);

            return (TEntity)retorno;

        }

        public IQueryable<TEntity> RecuperarTodos()
        {
            return this.dbSet;
        }

        public IQueryable Repositorio(string entidade)
        {
            return this.db.Repositorio(entidade);
        }

        public IQueryable<TEntity> Recuperar(System.Linq.Expressions.Expression<Func<TEntity, bool>> filtro = null, Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>> ordenar = null, string incluirPropriedades = "")
        {
            IQueryable<TEntity> query = this.dbSet.AsQueryable<TEntity>();

            foreach (var includeProperty in incluirPropriedades.Split
                (new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries))
            {
                query = query.Include(includeProperty);
            }

            if (ordenar != null)
            {
                query = ordenar(query);
            }

            return query;
        }

                

        public void Dispose()
        {
            throw new NotImplementedException();
        }
                
    }
}

#region
//public IEnumerable<TEntity> Recuperar(IEnumerable<Comum.Pesquisa.CampoPesquisa> parametros, Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>> ordenar = null)
//{
//    return this.Recuperar(parametros, ordenar, 0, 0);
//}

//public IEnumerable<TEntity> Recuperar(IEnumerable<Comum.Pesquisa.CampoPesquisa> parametros, Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>> ordenar = null, int pular, int quantidade)
//{
//    IQueryable<TEntity> query = this.dbSet;

//    foreach (CampoPesquisa campo in parametros)
//    {
//        query = this.MontarParametro(query, campo);
//    }

//    if (!string.IsNullOrEmpty(ordenar))
//    {
//        query.OrderBy(ordenar);
//    }

//    if (pular > 0)
//    {
//        query.Skip(pular);
//    }

//    if (quantidade > 0)
//    {
//        query.Take(quantidade);
//    }

//    return query.ToList();
//}

//public int SalvarDados()
//{
//    return this.db.SaveChanges();
//}

//public void Alterar(TEntity entidade)
//{
//    entidade.ChangeTracker.State = ObjectState.Modified;
//}

//private ObjectQuery<TEntity> MontarParametro(IQueryable<TEntity> query, CampoPesquisa campo)
//{
//    ObjectQuery<TEntity> retorno = null;


//    string auxQuery = string.Empty;                
//    Type tipo = typeof(TEntity);

//    switch (campo.TipoPesquisa)
//    {
//        case TipoPesquisa.Igual:
//            auxQuery = "it.{0} = @{1}";
//            break;
//        case TipoPesquisa.Like:
//            auxQuery = "it.{0} like @{1} + '%'";
//            break;
//        case TipoPesquisa.Maior:
//            auxQuery = "it.{0} > @{1}";
//            break;
//        case TipoPesquisa.Menor:
//            auxQuery = "it.{0} < @{1}";
//            break;
//        case TipoPesquisa.MaiorIgual:
//            auxQuery = "it.{0} >= @{1}";
//            break;
//        case TipoPesquisa.MenorIgual:
//            auxQuery = "it.{0} <= @{1}";
//            break;
//        case TipoPesquisa.Between:
//            if (!string.IsNullOrEmpty(campo.Valores[0]))
//            {
//                auxQuery = "it.{0} >= @{1}";
//            }

//            if (!string.IsNullOrEmpty(campo.Valores[1]))
//            {
//                auxQuery += "it.{0} <= @{1}";
//            }

//            break;
//        case TipoPesquisa.In:
//            auxQuery = "it.{0} IN (@{1})";

//            break;
//        case TipoPesquisa.Custom:
//            auxQuery = campo.Query;
//            break;
//    }


//    int aux = 0;
//    List<string> nomesParamentros = new List<string>();
//    foreach (string para in campo.Valores)
//    {
//        nomesParamentros.Add(string.Concat("p", query.Parameters.Count.ToString(), aux++));
//    }
//    Predicate<TEntity>.

//    retorno = query.Where((string.Format(auxQuery, campo.Campo, nomesParamentros));

//    return retorno;
//}



//public TEntity RecuperarPorID(int id)
//{
//    EdmMember keyMember = RecuperarKeyMember();
//    EntityKey entKey = new EntityKey(string.Concat(this.db.DefaultContainerName, ".", this.dbSet.EntitySet.Name), keyMember.Name, id);

//    object retorno;

//    this.db.TryGetObjectByKey(entKey, out retorno);

//    if (retorno != null)
//        return retorno as TEntity;
//    else
//        return null;

//}
#endregion